package zju.dgm.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import zju.dgm.dto.*;

import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lzy
 */
public class Serializer {

    private static final Logger logger = LoggerFactory.getLogger(Serializer.class);
    private static final ByteArrayOutputStream bo = new ByteArrayOutputStream();

    public synchronized static byte[] getBytesFromObject(Object obj) {
        if (obj == null) {
            return null;
        }

        return getJSONString(obj).getBytes(StandardCharsets.UTF_8);
    }

    public static <T> T getObjectFromBytes(byte[] data, Class<T> tClass) {
        if (data == null || data.length == 0) {
            return null;
        }
        return getObjectFromBytes(data, 0, data.length, tClass);
    }

    public static <T> T getObjectFromBytes(byte[] data, MyTR<T> tr) {
        if (data == null || data.length == 0) {
            return null;
        }
        return getObjectFromBytes(data, 0, data.length, tr);
    }

    /**
     * 给ZYC的演示方法，没有实际用处
     */
    public static void toZYC() {
        // 所有带泛型的变量都这样序列化
        Map<String, ServerInfo> map = new HashMap<>();
        map.put("asd", new ServerInfo());
        Map<String, ServerInfo> rebuild = Serializer.getObjectFromBytes(
                Serializer.getBytesFromObject(map),
                new MyTR<Map<String, ServerInfo>>() {
                } // 关键，把要转化的类型填在这里
        );
        // 可以正确获得类型信息
        System.out.println(rebuild.get("asd").getClass() == ServerInfo.class);

        // 一个类RegionMapInfo的示范
        RegionMapInfo<String, ServerInfo> regionMapInfo = new RegionMapInfo<>();
        regionMapInfo.setMap(new HashMap<String, List<ServerInfo>>() {{
            put("asd", Arrays.asList(new ServerInfo(), new ServerInfo()));
        }});
        RegionMapInfo<String, ServerInfo> rebuild2 = Serializer.getObjectFromBytes(
                Serializer.getBytesFromObject(regionMapInfo),
                new MyTR<RegionMapInfo<String, ServerInfo>>() {
                }// 注意这个写法
        );
        for (ServerInfo serverInfo : rebuild2.getMap().get("asd")) {
            System.out.println("类：" + serverInfo.getClass() + ",值：" + serverInfo);
        }
    }


    public static <T> T getObjectFromBytes(byte[] data, int offset, int len, Class<T> tClass) {

        if (data == null) {
            return null;
        }
        return JSONObject.parseObject(data, offset, len, StandardCharsets.UTF_8, tClass);
    }

    public static <T> T getObjectFromBytes(byte[] data, int offset, int len, MyTR<T> tr) {

        if (data == null) {
            return null;
        }

        return JSONObject.parseObject(data, offset, len, StandardCharsets.UTF_8, tr.getType());
    }

    public static String getJSONString(Object obj) {
        return JSONObject.toJSONString(obj);
    }

    public static <T> T getObjectFromJSON(String json, Class<T> tClass) {
        return JSONObject.parseObject(json, tClass);
    }

    public static List<StatEntryDTO> getDTOFromQueue(ConcurrentHashMap<String, PriorityQueue<RegionEntry>> queue) {
        if (queue == null) {
            return null;
        }
        List<StatEntryDTO> lst = new LinkedList<>();
        queue.forEach((k, v) -> {
            StatEntryDTO statEntryDTO = new StatEntryDTO();
            statEntryDTO.setTable(k);
            v.forEach(regionEntry -> {
                statEntryDTO.getInfos().add(regionEntry.getServer());
                statEntryDTO.getQTimes().add(regionEntry.getQTimes());
            });
            lst.add(statEntryDTO);
        });
        return lst;
    }

    public static List<StatEntryDTO> getDTOFromMap(HashMap<String, HashMap<ServerInfo, Integer>> map) {
        if (map == null) {
            return null;
        }
        List<StatEntryDTO> lst = new LinkedList<>();
        map.forEach((k, v) -> {
            StatEntryDTO statEntryDTO = new StatEntryDTO();
            statEntryDTO.setTable(k);
            v.forEach((serverInfo, qTimes) -> {
                statEntryDTO.getInfos().add(serverInfo);
                statEntryDTO.getQTimes().add(qTimes);
            });
            lst.add(statEntryDTO);
        });
        return lst;
    }

    public static ConcurrentHashMap<String, PriorityQueue<RegionEntry>> getQueueFromDTO(List<JSONObject> dtoList) {
        if (dtoList == null) {
            return null;
        }
        ConcurrentHashMap<String, PriorityQueue<RegionEntry>> queue = new ConcurrentHashMap<>();
        dtoList.forEach((_statEntryDTO) -> {
            StatEntryDTO statEntryDTO = _statEntryDTO.toJavaObject(StatEntryDTO.class);
            PriorityQueue<RegionEntry> pq = QueryStat.minHeapInstance();
            for (int i = 0; i < statEntryDTO.getInfos().size(); i++) {
                pq.add(new RegionEntry(statEntryDTO.getInfos().get(i), statEntryDTO.getQTimes().get(i)));
            }
            queue.put(statEntryDTO.getTable(), pq);
        });
        return queue;
    }

    public static HashMap<String, HashMap<ServerInfo, Integer>> getMapFromDTO(List<JSONObject> dtoList) {
        if (dtoList == null) {
            return null;
        }
        HashMap<String, HashMap<ServerInfo, Integer>> map = new HashMap<>();
        dtoList.forEach(_statEntryDTO -> {
            StatEntryDTO statEntryDTO = _statEntryDTO.toJavaObject(StatEntryDTO.class);
            HashMap<ServerInfo, Integer> one = new HashMap<>();
            for (int i = 0; i < statEntryDTO.getInfos().size(); i++) {
                one.put(statEntryDTO.getInfos().get(i), statEntryDTO.getQTimes().get(i));
            }
            map.put(statEntryDTO.getTable(), one);
        });
        return map;
    }


}
